﻿using Microsoft.Office.Interop.Excel;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using Rectangle = System.Drawing.Rectangle;

namespace TestTubeRecord
{
    //todo:已知问题：坐标数字完全删除后，会卡死

    public partial class Form1 : Form
    {
        public List<Record> Records { get; set; }

        DrawPanel panelGraph = new DrawPanel();
        PictureBox imgGraph = new PictureBox();

        public Form1()
        {
            InitializeComponent();
            ClearRecords();
            this.Text = $"试管编号记录程序 v{TtrHelper.GetVersion()}";

            txtStartId.KeyUp += TxtStartId_KeyUp;
            txtStartId.KeyPress += KeyPress;

            txtEndId.KeyUp += TxtEndId_KeyUp;
            txtEndId.KeyPress += KeyPress;

            txtCurrentId.KeyUp += TxtCurrentId_KeyUp;
            txtCurrentId.KeyPress += KeyPress;

            txtCurrentCode.KeyUp += TxtCurrentCode_KeyUp;
            txtCurrentCode.KeyPress += KeyPress;

            txtCurrentX.KeyUp += TxtCurrentX_KeyUp;
            txtCurrentX.KeyPress += KeyPress;

            txtCurrentY.KeyUp += TxtCurrentY_KeyUp;
            txtCurrentY.KeyPress += KeyPress;

            txtSearch.KeyUp += TxtSearch_KeyUp;

            txtXCount.KeyUp += TxtXCount_KeyUp;
            txtYCount.KeyUp += TxtYCount_KeyUp;

            this.gbGraph.Controls.Add(panelGraph);
            panelGraph.Width = this.gbGraph.Width;
            panelGraph.Controls.Add(imgGraph);
            panelGraph.Dock = DockStyle.Fill;
            panelGraph.AutoScroll = true;
            imgGraph.Width = panelGraph.Width;
            imgGraph.Height = panelGraph.Height;

            cbTubeOrder.SelectedIndex = 0;

            dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
        }

        private void TxtYCount_KeyUp(object sender, KeyEventArgs e)
        {
            CleanGraph();
        }

        private void TxtXCount_KeyUp(object sender, KeyEventArgs e)
        {
            CleanGraph();
        }

        private void TxtSearch_KeyUp(object sender, KeyEventArgs e)
        {
            var keyWord = txtSearch.Text.Trim();
            if (string.IsNullOrEmpty(keyWord))
            {
                RefreshData();
                return;
            }

            var searchRecords = Records.Where(z => z.Id.ToString().Contains(keyWord) || z.Code.Contains(keyWord)).ToList();
            dataGridView1.DataSource = new BindingList<Record>(searchRecords);
            dataGridView1.Refresh();
        }

        private void TxtCurrentY_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Control || e.KeyCode == Keys.Enter)
            {
                txtCurrentId.Focus();
                txtCurrentId.SelectAll();
            }

            CalcCurrentId();
        }


        private void TxtCurrentX_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Control || e.KeyCode == Keys.Enter)
            {
                txtCurrentY.Focus();
                txtCurrentY.SelectAll();
            }

            CalcCurrentId();
        }

        private void KeyPress(object sender, KeyPressEventArgs e)
        {
            if ((e.KeyChar < 48 || e.KeyChar > 57) && e.KeyChar != 8)
            {
                e.Handled = true;
            }
        }

        private void TxtCurrentId_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Control || e.KeyCode == Keys.Enter)
            {
                txtCurrentCode.Focus();
                txtCurrentCode.SelectAll();

                ulong.TryParse(txtCurrentId.Text, out ulong currentId);

                ShowMessage($"请扫描【{(currentId + 1)}】号试管【{txtCurrentX.Text}】列【{txtCurrentY.Text}】行");
            }
        }

        private void TxtCurrentCode_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Control || e.KeyCode == Keys.Enter)
            {
                var code = txtCurrentCode.Text.Trim();
                var saveFileNow = code == "8888";

                //添加记录
                if (!saveFileNow)
                {
                    //去重
                    if (ckbCheckRepeat.Checked)
                    {
                        var repeatedCode = Records.FirstOrDefault(z => z.Code == code);
                        if (repeatedCode != null)
                        {
                            ShowMessage($"重复录入条形码，请人工核验：{code}", KnownColor.Red);
                            txtCurrentCode.SelectAll();
                            return;
                        }
                    }
                   
                    var result = AddRecord();
                    if (!result)
                    {
                        return;
                    }
                }

                int.TryParse(txtTubeCount.Text, out int tubeCount);
                int.TryParse(txtXCount.Text, out int xCount);
                int.TryParse(txtCurrentX.Text, out int currentX);
                int.TryParse(txtCurrentY.Text, out int currentY);
                ulong.TryParse(txtCurrentId.Text, out ulong currentId);

                var currentIndex = CalcCurrentIndex();

                //准备下一个
                if (saveFileNow || (tubeCount != 0 && currentIndex >= tubeCount))
                {
                    //当前批次录入完毕
                    //TODO:给一个自动保存的选项
                    var saveSuccess = SaveFile(true);

                    if (!saveSuccess)
                    {
                        return;
                    }

                    txtStartId.Text = "";
                    txtStartId.Focus();

                    txtEndId.Text = "";

                    txtCurrentX.Text = "1";
                    txtCurrentY.Text = "1";
                }
                else
                {
                    if (currentX == xCount)
                    {
                        //准备换行
                        txtCurrentX.Text = "1";
                        txtCurrentY.Text = (currentY + 1).ToString();
                    }
                    else
                    {
                        //不换行
                        txtCurrentX.Text = (currentX + 1).ToString();
                    }

                    ShowMessage($"确认试管条形码：{code}\r\n请扫描【{(currentId + 1)}】号试管【{txtCurrentX.Text}】列【{txtCurrentY.Text}】行");
                }

                CalcCurrentId();

                //清空当前内容
                txtCurrentCode.Text = "";
            }
        }

        private void TxtEndId_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Control || e.KeyCode == Keys.Enter)
            {
                txtCurrentId.Focus();
                txtCurrentId.SelectAll();
            }
        }

        private void TxtStartId_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Control || e.KeyCode == Keys.Enter)
            {
                txtEndId.Focus();
                txtEndId.SelectAll();
            }
            else
            {
                ulong.TryParse(txtStartId.Text, out ulong startId);
                uint.TryParse(txtTubeCount.Text, out uint tubeCount);
                txtEndId.Text = (startId + tubeCount).ToString();

                CalcCurrentId();
            }
        }

        /// <summary>
        /// 清空数据
        /// </summary>
        private void ClearRecords()
        {
            txtCurrentX.Text = "1";
            txtCurrentY.Text = "1";
            txtCurrentCode.Text = "";

            CalcCurrentId();

            Records = new List<Record>();

            RefreshData();
        }

        /// <summary>
        /// 刷新数据
        /// </summary>
        private void RefreshData()
        {
            dataGridView1.DataSource = new BindingList<Record>(Records);
            dataGridView1.Refresh();
            dataGridView1.Columns[0].Frozen = true;
        }

        private void btnChooseFolder_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog path = new FolderBrowserDialog();
            path.ShowDialog();
            this.txtSaveFolder.Text = path.SelectedPath;
        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            SaveFile();
        }

        private void ShowMessage(string text, KnownColor color = KnownColor.Black)
        {
            lblState.Text = text;
            lblState.ForeColor = Color.FromKnownColor(color);
        }

        /// <summary>
        /// 添加记录
        /// </summary>
        private bool AddRecord()
        {
            if (!int.TryParse(txtCurrentX.Text, out int currentX))
            {
                ShowMessage("请输入正确的横坐标！", KnownColor.Red);
                txtCurrentX.Focus();
                txtCurrentX.SelectAll();
                return false;
            }

            if (!int.TryParse(txtCurrentY.Text, out int currentY))
            {
                ShowMessage("请输入正确的纵坐标！", KnownColor.Red);
                txtCurrentY.Focus();
                txtCurrentY.SelectAll();
                return false;
            }

            if (!ulong.TryParse(txtCurrentId.Text, out ulong currentId))
            {
                ShowMessage("请输入正确的编号！", KnownColor.Red);
                txtCurrentId.Focus();
                txtCurrentId.SelectAll();
                return false;
            }

            var currentCode = txtCurrentCode.Text;

            if (string.IsNullOrEmpty(currentCode))
            {
                ShowMessage("请输入正确的编码（条形码）！", KnownColor.Red);
                return false;
            }

            var record = Records.FirstOrDefault(z => z.PositionX == currentX && z.PositionY == currentY);
            if (record == null)
            {
                record = new Record() { PositionX = currentX, PositionY = currentY };
                Records.Add(record);
            }

            record.Id = currentId;
            record.Time = DateTime.Now;
            record.Code = currentCode;

            RefreshData();
            this.dataGridView1.FirstDisplayedScrollingRowIndex = this.dataGridView1.Rows.Count - 1;
            return true;
        }

        /// <summary>
        /// 计算当前 ID
        /// </summary>
        /// <param name="tryMoveNext">是否已经记录结束，尝试移动到下一个</param>
        private void CalcCurrentId()
        {
            //当前排序个数
            var currentIndex = CalcCurrentIndex();

            //当前记录ID
            ulong.TryParse(txtStartId.Text, out ulong startId);
            var currentId = currentIndex + startId - 1;
            txtCurrentId.Text = currentId.ToString();
        }

        /// <summary>
        /// 计算当前排序个数
        /// </summary>
        /// <returns></returns>
        private uint CalcCurrentIndex()
        {
            uint.TryParse(txtXCount.Text, out uint xCount);

            uint.TryParse(txtCurrentX.Text, out uint currentX);
            uint.TryParse(txtCurrentY.Text, out uint currentY);

            //当前排序个数（从 1 开始）
            var currentIndex = (currentY - 1) * xCount + currentX;

            DrawGraph(currentIndex);

            return currentIndex;
        }

        /// <summary>
        /// 获取当前托盘的排序编号
        /// </summary>
        /// <param name="currentIndex"></param>
        /// <param name="xCount"></param>
        /// <param name="yCount"></param>
        /// <returns></returns>
        public uint GetCurrentContainerIndex(uint currentIndex, uint xCount, uint yCount)
        {
            return currentIndex - (uint)(currentIndex / (xCount * yCount));
        }

        private void CleanGraph()
        {
            var g = this.imgGraph.CreateGraphics();

            //清理
            var brushClean = new SolidBrush(imgGraph.BackColor);
            g.FillRectangle(brushClean, 0, 0, imgGraph.Width, imgGraph.Height);
            g.Dispose();
        }

        /// <summary>
        /// 绘制试管
        /// </summary>
        /// <param name="currentIndex"></param>
        private void DrawGraph(uint currentIndex, bool scrollWhenOverflow = true)
        {
            var maxWidth = this.imgGraph.Width - 20/*预留滚动条*/;

            uint.TryParse(txtXCount.Text, out uint xCount);
            uint.TryParse(txtYCount.Text, out uint yCount);

            var containerTubeCount = xCount * yCount;//每个托盘的试管数量

            //计算多少托盘
            uint.TryParse(txtTubeCount.Text, out uint tubeCount);

            var totalContainerCount = 1u;//所有托盘数
            //var hideContainerCount = 0u;//隐藏托盘数
            if (tubeCount > 0)
            {
                totalContainerCount = (uint)((tubeCount - 1) / containerTubeCount) + 1;
                //hideContainerCount = (uint)((currentIndex - 1) / containerTubeCount);
            }

            //var leftContainerCount = totalContainerCount - hideContainerCount;//还剩下未处理完的托盘

            //半径
            var tubeRadius = (int)(maxWidth / Math.Max(xCount, 1)) - 3;

            var brushRed = new SolidBrush(Color.DarkRed);
            var brushGray = new SolidBrush(Color.Gray);
            var brushGreen = new SolidBrush(Color.Green);
            Pen penRed = new Pen(brushRed, 1);

            var tubeIndex = 0u;//正在渲染的试管索引
            var maxY = 0;//最大 y 坐标
            var currentTubeY = 0;//当前试管纵向位置

            List<DrawInfo> drawInfos = new List<DrawInfo>();
            var startNewContainer = false;//是否是一个托盘的开始

            for (int c = 0; c < totalContainerCount; c++)
            {
                for (int i = 0; i < yCount; i++)
                {
                    for (int j = 0; j < xCount; j++)
                    {
                        tubeIndex++;

                        var tubeOrderIndex = GetOrderedTubeIndex(j, i, xCount, yCount);

                        //X 和 Y 坐标（定位试管左上角）
                        var x = (tubeRadius + 3/*保留试管之间纵向的空隙*/) * tubeOrderIndex.XIndex;
                        var y = (int)((c * yCount * 1.4 * tubeRadius)/*多组托盘定位，保留一定间距*/
                                       + (tubeRadius + 3/*保留试管之间纵向的空隙*/) * tubeOrderIndex.YIndex);
                        maxY = y + 100;

                        //画外围框（空心）
                        drawInfos.Add(new DrawInfo(x, y, penRed, tubeRadius));

                        //如果超过数量则不渲染（留空的试管位置）
                        if (tubeIndex <= tubeCount)
                        {
                            //填充试管内部颜色

                            Brush tubeBrush;
                            if (tubeIndex < currentIndex)
                            {
                                tubeBrush = brushGray;//已经扫过
                            }
                            else if (tubeIndex == currentIndex)
                            {
                                tubeBrush = brushGreen;//正在扫
                                currentTubeY = y;
                                startNewContainer = j == 0;
                            }
                            else
                            {
                                tubeBrush = brushRed;//还没有扫
                            }

                            drawInfos.Add(new DrawInfo(x, y, tubeBrush, (int)(tubeRadius * 1)));
                        }
                    }
                }
            }

            this.imgGraph.Height = maxY + 100;

            Bitmap bmp = new Bitmap(this.imgGraph.Width, this.imgGraph.Height);

            //var g = this.imgGraph.CreateGraphics();
            var g = Graphics.FromImage(bmp);
            this.imgGraph.Image = bmp;

            //输出渲染结果
            foreach (var drawInfo in drawInfos)
            {
                drawInfo.Render(g);
            }

            penRed.Dispose();
            g.Dispose();


            //自动定位到活动的试管
            if (startNewContainer && currentTubeY > panelGraph.Height)
            {
                if (scrollWhenOverflow)
                {
                    panelGraph.VerticalScroll.Value = currentTubeY - panelGraph.Height / 4;
                }
            }

        }

        private void btnClearData_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("确定清空记录吗？您可能需要重新扫描本批次试管！", "警告", MessageBoxButtons.OKCancel, MessageBoxIcon.Asterisk) == DialogResult.OK)
            {
                ClearRecords();
                ShowMessage("数据清理完毕，请重新扫描本批次试管！");
            }
        }


        private void toolStripMenuItem2_Click(object sender, EventArgs e)
        {
            TtrHelper.GetSubForm<FormHowToUse>().Close();
            TtrHelper.GetSubForm<FormHowToUse>().Show();
        }

        private void 源代码ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            TtrHelper.GetSubForm<FormSourceCode>().Close();
            TtrHelper.GetSubForm<FormSourceCode>().Show();
        }

        private void toolStripMenuItem3_Click(object sender, EventArgs e)
        {
            MessageBox.Show($"当前版本：v{TtrHelper.GetVersion()}", "版本", MessageBoxButtons.OK);
        }

        #region Excel 操作


        /// <summary>
        /// 保存文件
        /// </summary>
        private bool SaveFile(bool clearRecords = true)
        {
            string excelFilePath = txtSaveFolder.Text.Trim();
            if (string.IsNullOrEmpty(excelFilePath))
            {
                ShowMessage("请提供正确的保存目录！", KnownColor.Red);
                return false;
            }

            ShowMessage("正在保存文件中，请准备下一批试管...");

            var app = new Microsoft.Office.Interop.Excel.Application();
            //_Workbook _wbk = app.Workbooks.Open(excelFilePath); //打开位于path的文件
            _Workbook _wbk = app.Workbooks.Add(); //新建一个workbook，可以保存为新的excel文件
            _Worksheet _sheet = (_Worksheet)_wbk.Sheets[1]; //Sheets下标从1开始而不是0
            try
            {

                excelFilePath = Path.Combine(excelFilePath, $"{txtStartId.Text}-{txtEndId.Text}.xlsx");

                //是否需要添加 Excel 的头
                bool needHeader = ckbSaveHeader.Checked;

                var rowCount = needHeader ? Records.Count + 1 : Records.Count;

                var i = 0;
                foreach (var record in Records)
                {
                    var firstRow = i == 0;

                    if (i == 0 && needHeader)
                    {
                        i++;//跳过第一个，留给 Header
                    }

                    var columnIndex = 1;//从 1 开始

                    if (firstRow)
                    {
                        ((Range)_sheet.Columns[columnIndex, Missing.Value]).ColumnWidth = record.Id.ToString().Length + 4;
                        ((Range)_sheet.Columns[columnIndex, Missing.Value]).NumberFormat = "@";
                        SetHeader(_sheet, needHeader, columnIndex, "编号");
                    }
                    _sheet.Cells[i + 1, columnIndex.GetExcelColumnName()] = record.Id.ToString();

                    columnIndex++;
                    if (firstRow)
                    {
                        ((Range)_sheet.Columns[columnIndex, Missing.Value]).ColumnWidth = Math.Max(record.Code.ToString().Length + 4, 15);
                        ((Range)_sheet.Columns[columnIndex, Missing.Value]).NumberFormat = "@";
                        SetHeader(_sheet, needHeader, columnIndex, "条码");
                    }
                    _sheet.Cells[i + 1, columnIndex.GetExcelColumnName()] = record.Code;

                    if (ckbSaveTime.Checked)
                    {
                        columnIndex++;

                        if (firstRow)
                        {
                            ((Range)_sheet.Columns[columnIndex, Missing.Value]).NumberFormat = "@";
                            ((Range)_sheet.Columns[columnIndex, Missing.Value]).ColumnWidth = 20;
                            SetHeader(_sheet, needHeader, columnIndex, "扫码时间");

                        }
                        _sheet.Cells[i + 1, columnIndex.GetExcelColumnName()] = record.Time.ToString();
                    }

                    var operatorName = txtOperator.Text;
                    if (!string.IsNullOrEmpty(operatorName))
                    {
                        columnIndex++;

                        if (firstRow)
                        {
                            ((Range)_sheet.Columns[columnIndex, Missing.Value]).NumberFormat = "@";
                            SetHeader(_sheet, needHeader, columnIndex, "操作人");
                        }
                        _sheet.Cells[i + 1, columnIndex.GetExcelColumnName()] = operatorName;
                    }

                    i++;
                }

                _wbk.SaveAs(excelFilePath);
                ShowMessage("保存完毕，请输入新批次起始编号");

                if (clearRecords)
                {
                    ClearRecords();
                }

                return true;
            }
            catch (Exception ex)
            {
                ShowMessage("错误信息：" + ex.Message, KnownColor.Red);
                return false;
            }
            finally
            {
                ClosePro(excelFilePath, app, _wbk);
            }
        }

        /// <summary>
        /// 设置表头
        /// </summary>
        /// <param name="_sheet"></param>
        /// <param name="needHeader"></param>
        /// <param name="columnIndex"></param>
        /// <param name="headerName"></param>
        private static void SetHeader(_Worksheet _sheet, bool needHeader, int columnIndex, string headerName)
        {
            if (needHeader)
            {
                _sheet.Cells[1, columnIndex.GetExcelColumnName()] = headerName;
            }
        }

        /// <summary>
        /// 关闭Excel进程
        /// </summary>
        /// <param name="excelPath"></param>
        /// <param name="excel"></param>
        /// <param name="wb"></param>
        public void ClosePro(string excelPath, Microsoft.Office.Interop.Excel.Application excel, Microsoft.Office.Interop.Excel._Workbook wb)
        {
            Process[] localByNameApp = Process.GetProcessesByName(excelPath);//获取程序名的所有进程
            if (localByNameApp.Length > 0)
            {
                foreach (var app in localByNameApp)
                {
                    if (!app.HasExited)
                    {
                        #region
                        ////设置禁止弹出保存和覆盖的询问提示框   
                        //excel.DisplayAlerts = false;
                        //excel.AlertBeforeOverwriting = false;

                        ////保存工作簿   
                        //excel.Application.Workbooks.Add(true).Save();
                        ////保存excel文件   
                        //excel.Save("D:" + "\\test.xls");
                        ////确保Excel进程关闭   
                        //excel.Quit();
                        //excel = null; 
                        #endregion
                        app.Kill();//关闭进程  
                    }
                }
            }
            if (wb != null)
                wb.Close(true, Type.Missing, Type.Missing);
            excel.Quit();
            // 安全回收进程
            System.GC.GetGeneration(excel);
        }


        #endregion

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            //CleanGraph();
            //DrawGraph(CalcCurrentIndex());
        }

        private TubeOrderIndex GetOrderedTubeIndex(int xIndex, int yIndex, uint xCount, uint yCount)
        {
            TubeOrderIndex tubeOrderIndex = null;
            switch (cbTubeOrder.SelectedIndex)
            {
                case 0:// "左下角开始":
                    tubeOrderIndex = new TubeOrderIndex(xIndex, (int)(yCount - 1 - yIndex));
                    break;
                case 1:// "右下角开始":
                    tubeOrderIndex = new TubeOrderIndex((int)(xCount - 1 - xIndex), (int)(yCount - 1 - yIndex));
                    break;
                case 2:// "左上角开始":
                    tubeOrderIndex = new TubeOrderIndex(xIndex, yIndex);
                    break;
                case 3:// "右上角开始":
                    tubeOrderIndex = new TubeOrderIndex((int)(xCount - 1 - xIndex), yIndex);
                    break;
                default:
                    ShowMessage("请选择正确的【试管拿取顺序】");
                    tubeOrderIndex = new TubeOrderIndex(0, 0);
                    break;
            }
            return tubeOrderIndex;
        }

    }
}
